package com.mymall.serve.utils;

import com.mymall.serve.config.redission.annotation.Lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 缓存工具类
 *
 * @author weiwei
 * @since 2020-04-11 19:40
 **/
@Slf4j
@Service
public class CacheUtil {
    public static final String ApplicationName = "mall-sys-";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public String getKey(String key) {
        Object obj = redisTemplate.opsForValue().get(ApplicationName + key);
        return obj == null ? null : String.valueOf(obj);
    }

    public boolean isEqualKaptchaWrapper(String key, String compareValue) {
        String cacheValue = getKey(key);
        if (cacheValue == null) {
            return false;
        }
        return cacheValue.equals(compareValue);
    }

    public void setValue(String key, String value, long timeSecond) {
        if (timeSecond <= 0) {
            redisTemplate.opsForValue().set(ApplicationName + key, value);
        } else {
            redisTemplate.opsForValue().set(ApplicationName + key, value, timeSecond, TimeUnit.SECONDS);
        }

    }

    public Boolean setValueIfAbsent(String key, String value, long timeSecond){
        if (timeSecond <= 0) {
            return redisTemplate.opsForValue().setIfAbsent(ApplicationName + key, value);
        } else {
            return redisTemplate.opsForValue().setIfAbsent(ApplicationName + key, value, timeSecond, TimeUnit.SECONDS);
        }
    }

    public Long increment(String key, long timeSecond) {

        Long increment = redisTemplate.opsForValue().increment(ApplicationName + key);
        redisTemplate.expire(ApplicationName + key, timeSecond, TimeUnit.SECONDS);
        return increment;
    }

    public void deleteKey(String key) {
        redisTemplate.delete(ApplicationName + key);
    }

    /**
     * 添加排名
     *
     * @param key
     * @param value
     */
    public void zadd(String key, String value, double score) {
        log.info("zadd,key:{},value:{},score:{}", key, value, score);
        redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 删除元素 zrem
     *
     * @param key
     * @param value
     */
    public void remove(String key, String value) {
        redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * score的增加or减少 zincrby
     *
     * @param key
     * @param value
     * @param score
     */
    public Double incrScore(String key, String value, double score) {
        log.info("zincrement,key:{},value:{},score:{}", key, value, score);
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * 查询value对应的score   zscore
     *
     * @param key
     * @param value
     * @return
     */
    public Double score(String key, String value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 判断value在zset中的排名  zrank
     * <p>
     * 积分小的在前面
     *
     * @param key
     * @param value
     * @return
     */
    public Long rank(String key, String value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 查询集合中指定顺序的值， 0 -1 表示获取全部的集合内容  zrange
     * <p>
     * 返回有序的集合，score小的在前面
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> range(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 查询集合中指定顺序的值和score，0, -1 表示获取全部的集合内容
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> rangeWithScore(String key, long start, long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 查询集合中指定顺序的值和score，0, -1 表示获取全部的集合内容
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> reverseRangeWithScores(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }

    /**
     * 查询集合中指定顺序的值  zrevrange
     * <p>
     * 返回有序的集合中，score大的在前面
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> revRange(String key, long start, long end) {

        return redisTemplate.opsForZSet().reverseRange(key, start, end);

    }

    /**
     * 根据score的值，来获取满足条件的集合  zrangebyscore
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> sortRange(String key, long min, long max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 返回集合的长度
     *
     * @param key
     * @return
     */
    public Long size(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 滑动窗口
     *
     * @param key          key
     * @param windowLength 窗口长度
     * @return 窗口内数据
     */
    public Long slideFlowCount(String key, int windowLength) {
        long current = System.currentTimeMillis();
        // 添加
        redisTemplate.opsForZSet().add(key, String.valueOf(current),current);
        // 清除过期的项目
        redisTemplate.opsForZSet().removeRangeByScore(key,0,current - windowLength*1000);
        // 获取窗口内的数量
        Long aLong = redisTemplate.opsForZSet().zCard(key);
        // 设置zset过期时间，避免冷用户持续占用内存
        // 过期时间应该等于时间窗口长度，再多宽限1s
        redisTemplate.expire(key,windowLength + 1,TimeUnit.SECONDS);
        return aLong;
    }

}
